#用户下单功能  视图函数集合
import datetime
import random
import time

from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse
from utils.bootstrap import BootStrapForm
from utils.encrypt import md5
from utils.link import filter_reverse
from utils.reponse import BaseResponse
from web import models
from django import forms
from django.core.exceptions import ValidationError, BadRequest
import re
from django.db.models import Q, F
from utils.pager import Pagination
from web.views.video import get_old_view_count
from django_redis import get_redis_connection
from django.conf import settings
from django.contrib import messages
#客户订单显示界面【使用客户账户登录】
def my_order_list(request):
    # 在视图函数中读取massge
    # 读取撤销订单页面设置的massga
    # print("调用my_order_list")
    # from django.contrib.messages.api import get_messages
    # 使用get_messages读取消息
    # messages = get_messages(request)
    # 使用循环实现读取之前设置的数据并删除:便利循环messages列表,获取所有messages信息
    # for msg in messages:
    #     print("读取撤销订单页面设置的massga",msg)

    # 获取订单表数据库数据 ：传入当前登录的客户id [request.nb_user.id]
    queryset = models.Order.objects.filter( active=1,customer_id=request.nb_user.id).order_by("-id")
    pager = Pagination(request=request, query_set=queryset)  # 实例化页码类
    return render(request, "my_order_list.html", {'pager':pager})

#客户订单添加界面 的Form
class MyOrderModelForm(BootStrapForm,forms.ModelForm):
    class Meta:
        model = models.Order
        fields = ["url","count"]
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        #读取价格策略 依据数量排序 从小到大
        queryset = models.PricePolicy.objects.all().order_by("count")
        #单价计算文本列表
        price_count_list = []
        #保存单价提示的列表
        text_count_list = []
        for item in queryset:
            unit_price = item.price / item.count #单价等于价格除以数量
            price_count_list.append([item.count,f">= {item.count} ￥{unit_price}/条",unit_price]) #添加到列表【数量，单价提示,便于计算的单价值】
            text_count_list.append(f">= {item.count} ￥{unit_price}/条")
        #判断有没有价格策略
        if text_count_list:
            #为数量count添加一个价格策略提示
            self.fields["count"].help_text = "、".join(text_count_list)
        else:
            self.fields["count"].help_text = "请联系管理员设置价格"
        #在外部可通过实例化.price_count_list 获取所有的策略 用于：判断输入的数值大于最低价格策略的数量；计算单价
        self.price_count_list = price_count_list
    #通过钩子方法校验数量
    def clean_count(self):
        count = self.cleaned_data["count"] #获取输入个数
        #空列表 表示没价格策略 直接返回 不允许验证通过
        if not self.price_count_list:
            return ValidationError("请联系管理员设置价格")
        # print("获取输入个数",count,type(count))
        min_count_limit = self.price_count_list[0][0] # 获取价格策略的最小数量
        # print("获取价格策略的最小数量", min_count_limit,type( min_count_limit))
        # 小于最低 抛出异常
        if count < min_count_limit:
            print("小于最低 抛出异常")
            raise ValidationError(f"需要大于最低数量{min_count_limit}")
        return count

#客户订单添加界面
def my_order_add(request):
    if request.method == "GET":
        form =  MyOrderModelForm()
        return render(request,"form.html",{"form":form})
    form = MyOrderModelForm(data=request.POST)
    # 校验失败
    if not form.is_valid():
        # 返回异常
        return render(request, "form.html", {"form": form})
    # 1获取url 数量
    video_url = form.cleaned_data.get('url')
    count = form.cleaned_data.get('count')

    # 1.1 通过爬虫获取网络地址的源播放量 https://w.yangshipin.cn/video?type=0&vid=y000088hru8
    status, old_view_count = get_old_view_count(video_url) # 是否成功，播放量多少
    if not status:  # 如果视频源播放量获取不了
        form.add_error("count", "url源播放量获取失败")  # 在count下面添加异常
        return render(request, "form.html", {"form": form})
    print("播放量",old_view_count)

    # 2 获取单价 计算原价
    for idx in range(len(form.price_count_list)-1,-1,-1): #按照倒叙从大到小获取数据索引
       limit_count,a,unit_price  = form.price_count_list[idx] #解包
       # 从最大的开始比较方便确定单价
       if count >= limit_count:
            break
    total_price = unit_price * count  #计算原价 返回类型是 decimal.Decimal
    print("单价是", unit_price,"原价",type(total_price))
    try:
        #3 找到当前客户级别，计算折扣
        with transaction.atomic():  # 事务
            #在用户表获取当前客户后 加锁
            cus_objexct = models.Customer.objects.filter(id=request.nb_user.id).select_for_update().first()
            #链表level取得折扣 cus_objexct.level.percent/100
            real_price = total_price * cus_objexct.level.percent/100
            print("折后", real_price)
            #4 查找账户余额 看够不够 cus_objexct.balance 【decimal类型】
            if cus_objexct.balance < real_price: # 账户余额 小于折后价格
                form.add_error("count","账户余额不足") #在count下面添加异常
                return  render(request, "form.html", {"form": form})

            # 5 创建订单 【事务】
            # 5.1 生成订单号 随机数+当前时间
            while True:
                rand_number = random.randint(10000000,99999999) #随机8位数
                ctime = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f") #获取当前时间的年、月、日时分秒毫秒
                oid = f"{ctime}{rand_number}" #拼接得到订单号
                exi = models.Order.objects.filter(oid=oid).exists() #看数据库有没有相同的订单号
                if not exi: #不存在退出循环生成
                    break
            print("订单号",oid)
            # 5.2 填写客户id = 当前登录用户ID request.nb_user.id
            # 给数据库字段传值
            form.instance.oid = oid #传订单号
            form.instance.price = total_price #传原价
            form.instance.real_price = real_price  # 传折后价格
            form.instance.old_view_count = old_view_count #传源播放量
            form.instance.customer_id = request.nb_user.id
            form.save()
            # 6当前客户账户扣款 需要加锁 【事务】使用F函数在原来的数值上更新
            models.Customer.objects.filter(id=request.nb_user.id).update(balance = F("balance")-real_price)
            #7 在交易记录的表:新建记录 【事务】
            models.TransactionRecord.objects.create(
                charge_type=3,
                customer_id= request.nb_user.id,
                amount=real_price,
                order_oid=oid
            )
            # time.sleep(2)
            #8：任务写入rides队列
            conn = get_redis_connection("default") #获取redis链接
            # redis建立列表 YANG_TASK_QUEUE
            conn.lpush(settings.QUEUE_TASK_NAME,oid)
    except Exception as e:
        form.add_error("count", "创建订单异常")  # 在count下面添加异常
        return render(request, "form.html", {"form": form})
    return redirect("/my/order/list/")

# 客户订单撤销订单函数
def my_order_cancel(request,pk):
    # 0. 获取订单信息 看是否在数据库 [获取订单信息:id,激活,订单状态待执行]
    order_object = models.Order.objects.filter(id=pk, active=1,status=1,customer_id=request.nb_user.id).first()
    if not order_object: #订单不存在
        #添加messages提示 settings.MESSAGE_DANDER_TAG为自定义错误类型为配合前端展示
        messages.add_message(request, settings.MESSAGE_DANDER_TAG, "订单不存在或已经撤单")
        return redirect("/my/order/list/")
    try:
        #数据库操作需要加事务
        with transaction.atomic():
            #获取当前登录的用户信息 加锁select_for_update
            cus_object = models.Customer.objects.filter(id=request.nb_user.id).select_for_update().first()
            # 1 订单表里面更新订单状态 5已撤单
            order_object.status = 5
            order_object.save()
            #2 归还余额 通过用户表找"余额" 然后添加订单表的"折扣后的价格"
            models.Customer.objects.filter(id=request.nb_user.id).update(balance=F("balance") + order_object.real_price)
            #3交易记录表 产生交易记录
            models.TransactionRecord.objects.create(
                charge_type=5,
                customer_id=request.nb_user.id,
                amount=order_object.real_price,
                order_oid=order_object.oid
            )
            #4 撤单成功
            # 使用messages.add_message 添加数据(请求对象,消息级别,消息内容)
            # 存入messages以列表形式保存[删除成功,操作失败]
            messages.add_message(request, messages.SUCCESS, "撤单成功")
    except Exception as e:
        messages.add_message(request, settings.MESSAGE_DANDER_TAG, f"撤单失败:{e}")
        return redirect("/my/order/list/")

    return redirect("/my/order/list/") #重定向URL到客户订单显示界面